#
#   \brief   Parameter Generator
#
#   \author  Michael J. Hohmann <michael.hohmann@linde-mh.de>
#   \author  Joerg Treisch <Joerg.Treisch@linde-mh.de>
#
#   Synopsis: python.exe paragen.py  configfile.txt path
#
# *****************************************************************************

import sys
import time
import os, errno
import md5


#def get_index( parameter ):
#   rv=[]
#   for item in parameter.keys():
#       rv.append( item['index'] )
#   rv.sort()
#   return rv


def check_struct_entries(prefix, parameter,cnt):
        cnt_tmp = get_parameter_count( prefix , parameter, 3 )
        if cnt != 0: # ignore first or only one spe struct
            if cnt != cnt_tmp : # more spe structs and not same entry count
                print 'entries of variables in struct of object " ' + key + ' " are different to other objects '',\n'
                rv =  'entries: ' + str(cnt_tmp) +' in  ' + key +'.h'
                print rv
                raise AssertionError("entries of variables in struct are different to other objects")
        cnt = cnt_tmp
#   rv = 'spe count: ' + str(cnt)
#       print rv
        return cnt

def checkForMonPara(prefix):
        if prefix[0:3] == 'mon':
#           print prefix
           return True
        else:
           return False

def create_struct( name, items, cnt, indent =4 ):
    rv='/* entries: ' +str(cnt)+'   */ \n'
    rv+='struct {:s}\n'.format(name)
    rv+='{\n'
    for item in items:
        rv+='{:s}{:s};\n'.format(indent*' ',item)
    rv+='}; \n'
    return rv

"""
def create_struct_versioninfo( name, items,indent =4 ):
    rv='static const struct paragen_version_header PARAGEN_PT {:s}=\n'.format(name)
    rv+='{\n'
    first=True
    for item in items:
        if not first:
            rv+=',\n'
        rv+='{:s}{:s}'.format(indent*' ',item)
        first=False
    rv+='\n};\n'
        #print rv
    return rv
"""

def create_parameter_info(prefix, parameter_list, indent=4):
    cnt=0
    #if True == checkForMonPara(prefix):
    #    return "/* Mon-parameter needs no parameter_info */"
    items=[]
    for parameter in parameter_list:
        cnt=cnt+1
        items.append( 'struct {:s} {:s}' .format(parameter['type'],parameter['name']))
    return create_struct('{:s}_parameter_info'.format(prefix),items,cnt, indent)

def create_parameter_nv(prefix, indent=4):
    if True == checkForMonPara(prefix):
        return "/* Mon-parameter needs no parameter_nv */"
        #items=[ 'struct {:s}_parameter parameter'.format(prefix) ]
    else:   
        items=[ 'struct paragen_validation_header validation','struct {:s}_parameter parameter'.format(prefix) ]
    return create_struct( '{:s}_parameter_nv'.format(prefix),items, 7, indent )


def create_default_parameter(prefix, parameter_list, indent=4):
    if True == checkForMonPara(prefix):
        return "/* Mon-parameter needs no default_parameter */"
    rv='static const struct {:s}_parameter {:s}_default_parameter=\n'.format(prefix,prefix)
    rv+='{\n'
    first=True
    for parameter in parameter_list:
           if not first:
               rv+=',\n'
           rv_min = '{:s}{:s}'.format(indent*' ',str(parameter['min']))
           rv_def = '{:s}{:s}'.format(indent*' ',str(parameter['default']))
           rv_max = '{:s}{:s}'.format(indent*' ',str(parameter['max']))

           if (parameter['type']) != 'paragen_type_ch_mm':
                # checks default in range with min/max value
                if int(rv_min) > int(rv_def):
                        print "default is lower than min \n"
                        print '{:s}'.format(prefix)
                        print '{:s}{:s} \n'.format(indent*' ',str(parameter['name']))
                        print rv_min, rv_def, rv_max + '\n'
#               rv+=" default is lower than min! "
                        raise AssertionError("default is lower than min!")
                if int(rv_max) < int(rv_def):
                        print "default is higher than max \n"
                        print '{:s}'.format(prefix)
                        print '{:s}{:s} \n'.format(indent*' ',str(parameter['name']))
                        print rv_min, rv_def, rv_max + '\n'
#               rv+=" default is higher than max! "
                        raise AssertionError("default is higher than max!")
                rv+='{:s}{:s},{:s}//{:s}'.format(indent*' ',str(parameter['default']), ' '*(30-len(str(parameter['default'])) - 4), str(parameter['name']))
           else:
                # different checks due default is string and min/max value are size of string
                if int(rv_min) != int(rv_max):
                        print "size of string is different for min/max \n"
                        print '{:s}'.format(prefix)
                        print '{:s}{:s} \n'.format(indent*' ',str(parameter['name']))
                        print rv_min, rv_max + '\n'
                        raise AssertionError("size of string is different for min/max!")
                # len - 3 due to two quotation marks and string endsign
                if (len(rv_def)-2) != int(rv_max):
                        print "size of string is different to min/max \n"
                        print '{:s}'.format(prefix)
                        print '{:s}{:s} \n'.format(indent*' ',str(parameter['name']))
                        print rv_def + '\n'
                        print rv_min, len(rv_def), rv_max + '\n'
                        raise AssertionError("size of string is different to min/max!")
                rv+='{:s}"{:s}",{:s}//{:s}'.format(indent*' ',str(parameter['default']), ' '*(30-len(str(parameter['default'])) - 6), str(parameter['name']))


           first=False
    rv+='\n'
    rv+='};\n'
    return rv;

"""
def create_versioninfo(prefix, parameter_list, indent=4):
        for parameter in parameter_list:
                rv =str(parameter['version'])   # version
        rv1= '0x00000000'                       # u32_reserved
        rv2='"' + prefix +'"'                   # obj_name
    items=[ rv, rv1 , rv2 ]
    return create_struct_versioninfo( '{:s}_versioninfo'.format(prefix),items, indent )
"""

def create_info(prefix, parameter_list, indent=4 ):
    #if True == checkForMonPara(prefix):
    #    return "/* Mon-parameter needs no info */"
    rv='static const struct {:s}_parameter_info {:s}_info=\n'.format(prefix,prefix)
    rv+='{\n'
    first=True
    for parameter in parameter_list:
        if not first:
            rv+=',\n'
        any=parameter.copy()
        any['prefix']=prefix
        any['indent']=indent*' '
        rv+='{indent}{{ {{ {type:s}, offsetof(struct {prefix:s}_parameter, {name:s}), U32_CONST(0x{read:08x}),U32_CONST(0x{write:08X}) }}, {min!s}, {max!s},{increment!s} }}'.format(**any)
        first=False
    rv+='\n'
    rv+='};\n'
    return rv;


def create_index(prefix, parameter_list, indent=4 ):
    #if True == checkForMonPara(prefix):
    #    return "/* Mon-parameter needs no setup_parameter */"
    rv='static const struct paragen_index  {prefix:s}_index[]=\n'.format(prefix=prefix)
    rv+='{\n'
    first=True
    test = True
    rv_test_old = 0
    rv_test     = 0
    for parameter in parameter_list:
        if not first:
            rv+=',\n'
        any=parameter.copy()
        any['prefix']=prefix
        any['indent']=indent*' '
        rv+='{indent:s}{{ 0x{index:08X}, (struct paragen_type_header*) &{prefix:s}_info.{name} }}'.format( **any)
                #print '{indent:s}{{0x{index:08X} {prefix:s}}}'.format( **any)
        rv_test = '{indent:s}{{0x{index:08X}}}'.format( **any)
        rv_test = rv_test[4:14]
        #print rv_test
        if rv_test_old == rv_test:
             print '{indent:s}{{0x{index:08X} {prefix:s} {name}}}'.format( **any)
             print "id is not unique for object"+',\n'
             raise AssertionError("id is not unique for object")
        if rv_test_old > rv_test:
             print '{indent:s}{{0x{index:08X} {prefix:s} {name}}}'.format( **any)
             print "ids not in ascending order"+',\n'
             raise AssertionError("ids not in ascending order")
        rv_test_old = rv_test
        first=False
    rv+='\n'
    rv+='};\n'
    return rv;


def create_index_monitoring_parameter(prefix, parameter_list, indent=4 ):
    if False == checkForMonPara(prefix):
        return "/* no monitoring_parameter */"
    rv='static const struct paragen_index {prefix:s}_index[]=\n'.format(prefix=prefix)
    rv+='{\n'
    first=True
    test = True
    rv_test_old = 0
    rv_test     = 0
    for parameter in parameter_list:


        basetypes={
                'paragen_type_u8_mm':'U8',
                'paragen_type_u16_mm':'U16',
                'paragen_type_u32_mm':'U32',
                'paragen_type_s8_mm':'S8',
                'paragen_type_s16_mm':'S16',
                'paragen_type_s32_mm':'S32',
                'paragen_type_f32_mm':'F32',
                'paragen_type_ch_mm':'CH'
            }
        items=[]

        if not first:
            rv+=',\n'
        any=parameter.copy()
        any['prefix']=prefix
        any['indent']=indent*' '
        any['types']=basetypes[parameter['type']]
        rv+='{indent:s}{{ 0x{index:08X}, (void *)&mon_parameter_ram.{name}, sizeof({types}) }}'.format( **any )
                #
        rv_test = '{indent:s}{{0x{index:08X}}}'.format( **any)
        rv_test = rv_test[4:14]
        #print rv_test
        if rv_test_old == rv_test:
             print '{indent:s}{{0x{index:08X} {prefix:s} {name}}}'.format( **any)
             print "id is not unique for object"+',\n'
             raise AssertionError("id is not unique for object")
        if rv_test_old > rv_test:
             print '{indent:s}{{0x{index:08X} {prefix:s} {name}}}'.format( **any)
             print "ids not in ascending order"+',\n'
             raise AssertionError("ids not in ascending order")
        rv_test_old = rv_test
        #
        first=False
    rv+='\n'
    rv+='};\n'
    return rv;


def create_ram_parameter(prefix, indent=4):
    rv='struct {:s}_parameter {:s}_parameter_ram;\n'.format(prefix,prefix)
    return rv;


def create_object(prefix, indent=4, parameter=[], fingerprint=None ):
        fingerprint_list=[]
        for item in fingerprint:
            fingerprint_list.append( ord(item) )

        indent=indent*' '
        rv='const struct paragen_object {prefix:s}_obj =\n'.format(prefix=prefix)
        rv+='{\n'
        if False == checkForMonPara(prefix):
            rv+='{:s}{:s}_SD,\n'.format( indent, prefix.upper())
        else:
            rv+='{:s}0,\n'.format( indent)
        rv+='{:s}(void*) &{:s}_parameter_ram,\n'.format( indent,  prefix )
        if False == checkForMonPara(prefix):
            rv+='{:s}(U32) {:s}_ADDRESS,\n'.format( indent,  prefix.upper() )  
        else:
            rv+='{:s}0,\n'.format( indent)
             
        rv+='{:s}{:s}_index,\n'.format( indent, prefix)
                
            

        rv+='{:s}(struct paragen_type_header *) &{:s}_info,\n'.format( indent, prefix) 

        
        if False == checkForMonPara(prefix):
            rv+='{:s}(void*) &{:s}_default_parameter,\n'.format( indent, prefix) 
            rv+='{:s}sizeof( struct {:s}_parameter ),\n'.format( indent, prefix)
            
        else:
            rv+='{:s}0,\n'.format( indent)
            rv+='{:s}0,\n'.format( indent)     
           
        rv+='{:s}sizeof {:s}_index /sizeof(struct paragen_index ),\n'.format( indent, prefix)   
        
        rv+='{:s}{{'.format( indent)
        seperator=''
        for item in fingerprint:
            rv+='{:s}0x{:02X}'.format(seperator,ord(item))
            seperator=','
        rv+='} /* finger print */ \n'
        rv+='};\n'
        return rv

def create_parameter(prefix, parameter_list, cnt, indent=4 ):
    cnt=0
    #if True == checkForMonPara(prefix):
    #    return "/* Mon-parameter needs no own parameter gets extern parameter */"
    basetypes={
        'paragen_type_u8_mm':'U8',
        'paragen_type_u16_mm':'U16',
        'paragen_type_u32_mm':'U32',
        'paragen_type_s8_mm':'S8',
        'paragen_type_s16_mm':'S16',
        'paragen_type_s32_mm':'S32',
        'paragen_type_f32_mm':'F32',
        'paragen_type_ch_mm':'CH'
    }
    items=[]
    for parameter in parameter_list:
        if (basetypes[parameter['type']]) == 'CH':
           items.append( '{:s}  {:s}[{:d}]'.format( basetypes[parameter['type']], parameter['name'] , parameter['max']))
        else:
           items.append( '{:s} {:s}'.format( basetypes[parameter['type']], parameter['name']))
        cnt=cnt+1
    return create_struct( '{:s}_parameter'.format(prefix),items, cnt, indent )

def get_parameter_count(prefix, parameter_list, indent=4 ):
    cnt=0
    if True == checkForMonPara(prefix):
        return "/* Mon-parameter needs no own parameter gets extern parameter */"
    basetypes={
        'paragen_type_u8_mm':'U8',
        'paragen_type_u16_mm':'U16',
        'paragen_type_u32_mm':'U32',
        'paragen_type_s8_mm':'S8',
        'paragen_type_s16_mm':'S16',
        'paragen_type_s32_mm':'S32',
        'paragen_type_f32_mm':'F32',
        'paragen_type_ch_mm':'CH'
    }
    items=[]
    for parameter in parameter_list:
        items.append( '{:s} {:s}'.format( basetypes[parameter['type']], parameter['name']))
        cnt=cnt+1
    return cnt


def create_c_file( prefix, parameter, fingerprint, indent ):
    fingerprint_str=''
    for item in fingerprint:
        fingerprint_str+='{:02x}'.format(ord(item)).upper()
        
    rv='''/**
*  \\file {prefix:s}.c
*
*  \\brief  {prefix:s} parameter.
*
*  \\author   paragen.py
*  \\author   Michael J. Hohmann <michael.hohmann@linde-mh.de>
*
*
*/

/*===============================================[ private includes  ]================================================*/
#include "{prefix:s}.h"
#include <stddef.h>
#include "../cfg/{prefix}_cfg.h"

/*===============================================[ private defines   ]================================================*/
#ifndef {prefix_uper:s}_FINGERPRINT_0x{fingerprint_str:s}
#error /* header has wrong version */
#endif
/*===============================================[ private datatypes ]================================================*/

'''.format( prefix=prefix, prefix_uper=prefix.upper(), fingerprint_str=fingerprint_str.upper() )
    #rv+=create_versioninfo( prefix , parameter , indent )
    #rv+='\n'
    rv+=create_parameter_info( prefix , parameter, indent )
    rv+='\n'
    #rv+=create_parameter_nv( prefix , indent )
    #rv+='\n'
    rv+='/*===============================================[ private variables ]================================================*/\n'
    rv+='\n'
    rv+=create_default_parameter( prefix , parameter, indent )
    rv+='\n'
    rv+=create_info( prefix , parameter, indent )
    rv+='\n'
    rv+=create_index( prefix , parameter, indent )
    rv+='\n'
    rv+=create_ram_parameter(prefix,indent)
    rv+='\n'
    #rv+=create_index_monitoring_parameter( prefix , parameter, indent )
    #rv+='\n'       
    rv+='/*===============================================[ public variables  ]================================================*/\n'
    rv+='\n'
    rv+='/** Parameter pointer\n'
    rv+='*  Pointer to read the Parameter from application\n'
    rv+='*/\n'
    rv+='struct {prefix:s}_parameter const * const {prefix:s} = &{prefix:s}_parameter_ram;\n'.format(prefix=prefix)
    rv+='\n'
    rv+=create_object( prefix , indent, parameter, fingerprint )
    rv+='\n'
    rv+='/*===============================================[ private functions ]================================================*/\n'
    rv+='\n'
    rv+='/*====================================================================================================================*/\n'
    rv+='\n'
    rv+='''
/***********************************************************************************************************************
*                             Copyright {:d} Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
'''.format( time.gmtime().tm_year )
    return rv



def create_h_file( prefix, parameter, fingerprint, indent ):
    fingerprint_str=''
    for item in fingerprint:
        fingerprint_str+='{:02x}'.format(ord(item))

    rv='''/**
*  \\file {prefix:s}.h
*
*  \\brief
*
*  \\author   paragen.py
*  \\author   Michael J. Hohmann <michael.hohmann@linde-mh.de>
*  \\author   Joerg.Treisch      <joerg.treisch@linde-mh.de>
*
*/

#ifndef {upper_prefix:s}_H_INCLUDED
#define {upper_prefix:s}_H_INCLUDED

/*================================================[ public includes  ]================================================*/
#include "../cfg/prj.h"
#include "../paragen/paragen.h"
/*================================================[ public defines   ]================================================*/
#define {upper_prefix:s}_FINGERPRINT_0x{fingerprint_str:s}

/*================================================[ public datatypes ]================================================*/
'''.format(prefix=prefix, upper_prefix=prefix.upper(), fingerprint_str=fingerprint_str.upper() )
    rv+='\n'      
    rv+=create_parameter( prefix , parameter, indent )
    rv+='\n'
    rv+=create_parameter_nv( prefix , indent )
    rv+='\n'  	
	
    rv+='''
/*================================================[ public variables ]================================================*/

extern const struct paragen_object {prefix:s}_obj;
extern struct {prefix:s}_parameter const * const {prefix:s};

/*================================================[ public functions ]================================================*/

/*====================================================================================================================*/

#endif

/***********************************************************************************************************************
*                             Copyright {year:d} Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
'''.format(prefix=prefix, year=time.gmtime().tm_year )

    return rv



def create_dir( dirname ):
    try:
        os.makedirs(dirname)
        os.makedirs(dirname)
    except OSError, e:
        if e.errno != errno.EEXIST:
            raise


def create_paragen_files(path, prefix, parameter):
    create_dir( path )
    cfilename=os.path.join(path,prefix+'.c')
    hfilename=os.path.join(path,prefix+'.h')

    outfile=open( cfilename,"w+" )

    m = md5.new()
    m.update(str(parameter))
    fingerprint=m.digest()

    outfile.write( create_c_file( prefix, parameter,fingerprint, 3 ) )
    outfile.close()

    outfile=open( hfilename,"w+" )
    outfile.write( create_h_file( prefix, parameter,fingerprint, 3 ) )
    outfile.close()

def linepack( **args ):
    return args


def getname( item ):
    return item['type']

def import_filter( filename ):
    rv={}
    parafile=file( filename,"r")
    for line in parafile.readlines():
        line=line.strip()
        if line=="" or line[0]=='#':
            pass
        else:
            x=eval('linepack({:s})'.format(line))
            smashedname=x['name'].split(':')
            x['name']=smashedname[1]
            if smashedname[0] in rv:
                rv[smashedname[0]].append(x)
            else:
                rv[smashedname[0]]=[x,]    
    return rv


if __name__=='__main__':

    print "MAKE: paragen.py: Generating PARAGEN-Files (*.c/*.h) from " + sys.argv[1]

    parameter = import_filter( sys.argv[1] )

    if len(sys.argv)==2:
        sys.argv.append('.')

    for key in parameter.keys():
        create_paragen_files( os.path.join( os.getcwd(), sys.argv[2]),key, parameter[key])
